#ifndef MEM_UTIL_H
#define MEM_UTIL_H
/*
HANDLE CreateFileA(
    [in]           LPCSTR                lpFileName,
    [in]           DWORD                 dwDesiredAccess,
    [in]           DWORD                 dwShareMode,
    [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    [in]           DWORD                 dwCreationDisposition,
    [in]           DWORD                 dwFlagsAndAttributes,
    [in, optional] HANDLE                hTemplateFile
    );
HANDLE OpenFileMappingW(
    [in] DWORD   dwDesiredAccess,
    [in] BOOL    bInheritHandle,
    [in] LPCWSTR lpName
    );
HANDLE CreateFileMapping(
    HANDLE hFile,
    LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
    DWORD flProtect,
    DWORD dwMaximumSizeHigh,
    DWORD dwMaximumSizeLow,
    LPCTSTR lpName
);
LPVOID MapViewOfFile(
    [in] HANDLE hFileMappingObject,
    [in] DWORD  dwDesiredAccess,
    [in] DWORD  dwFileOffsetHigh,
    [in] DWORD  dwFileOffsetLow,
    [in] SIZE_T dwNumberOfBytesToMap
    );
HANDLE OpenProcess(
    [in] DWORD dwDesiredAccess,
    [in] BOOL  bInheritHandle,
    [in] DWORD dwProcessId
    );
typedef struct _MEMORY_BASIC_INFORMATION {
    PVOID  BaseAddress;
    PVOID  AllocationBase;
    DWORD  AllocationProtect;
    WORD   PartitionId;
    SIZE_T RegionSize;
    DWORD  State;
    DWORD  Protect;
    DWORD  Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

SIZE_T VirtualQuery(
    [in, optional] LPCVOID                   lpAddress,
    [out]          PMEMORY_BASIC_INFORMATION lpBuffer,
    [in]           SIZE_T                    dwLength
    );
SIZE_T VirtualQueryEx(
    [in]           HANDLE                    hProcess,
    [in, optional] LPCVOID                   lpAddress,
    [out]          PMEMORY_BASIC_INFORMATION lpBuffer,
    [in]           SIZE_T                    dwLength
    );
LPVOID VirtualAllocEx(
    [in]           HANDLE hProcess,
    [in, optional] LPVOID lpAddress,
    [in]           SIZE_T dwSize,
    [in]           DWORD  flAllocationType,
    [in]           DWORD  flProtect
    );
BOOL VirtualFreeEx(
    [in] HANDLE hProcess,
    [in] LPVOID lpAddress,
    [in] SIZE_T dwSize,
    [in] DWORD  dwFreeType
    );
BOOL WriteProcessMemory(
    [in]  HANDLE  hProcess,
    [in]  LPVOID  lpBaseAddress,
    [in]  LPCVOID lpBuffer,
    [in]  SIZE_T  nSize,
    [out] SIZE_T  *lpNumberOfBytesWritten
    );
BOOL ReadProcessMemory(
    [in]  HANDLE  hProcess,
    [in]  LPCVOID lpBaseAddress,
    [out] LPVOID  lpBuffer,
    [in]  SIZE_T  nSize,
    [out] SIZE_T  *lpNumberOfBytesRead
    );
HANDLE CreateRemoteThread(
    [in]  HANDLE                 hProcess,
    [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
    [in]  SIZE_T                 dwStackSize,
    [in]  LPTHREAD_START_ROUTINE lpStartAddress,
    [in]  LPVOID                 lpParameter,
    [in]  DWORD                  dwCreationFlags,
    [out] LPDWORD                lpThreadId
    );
LPVOID VirtualAlloc(
    [in, optional] LPVOID lpAddress,
    [in]           SIZE_T dwSize,
    [in]           DWORD  flAllocationType,
    [in]           DWORD  flProtect
    );
BOOL VirtualFree(
    [in] LPVOID lpAddress,
    [in] SIZE_T dwSize,
    [in] DWORD  dwFreeType
    );
SIZE_T VirtualQuery(
    [in, optional] LPCVOID                   lpAddress,
    [out]          PMEMORY_BASIC_INFORMATION lpBuffer,
    [in]           SIZE_T                    dwLength
    );
SIZE_T VirtualQueryEx(
    [in]           HANDLE                    hProcess,
    [in, optional] LPCVOID                   lpAddress,
    [out]          PMEMORY_BASIC_INFORMATION lpBuffer,
    [in]           SIZE_T                    dwLength
    );
*/


#include<windows.h>
namespace xy {
    class Exception {
            LPCTSTR msg;
        public:
        Exception(LPCTSTR msg) {
            this->msg = msg;
        }
        LPCTSTR what() {
            return this->msg;
        }
    }
    ;
    class File {
        LPCTSTR file_name=NULL;
        HANDLE file_handle=INVALID_HANDLE_VALUE;
        public:
                File(LPCTSTR file_name=NULL) {
            this->file_name = file_name;
            this->file_handle = CreateFile(this->file_name,
                                                       GENERIC_ALL,
                                                       0,
                                                       NULL,
                                                       OPEN_ALWAYS,
                                                       FILE_ATTRIBUTE_NORMAL,
                                                       NULL
                                                       );
            if(this->file_handle == INVALID_HANDLE_VALUE) {
                throw Exception(TEXT("打开文件失败"));
            }
        }
        LPCTSTR get_file_name() {
            return this->file_name;
        }
        HANDLE get_handle() {
            return this->file_handle;
        }
        void close_handle() {
            CloseHandle(this->file_handle);
            this->file_handle = INVALID_HANDLE_VALUE;
        }
        virtual ~File() {
            if(this->file_handle != INVALID_HANDLE_VALUE) {
                this->close_handle();
            }
        }
    }
    ;
    //template<class T>
    class MapMemory final{
            HANDLE map_handle = NULL;
            File *file = NULL;
            DWORD64 size = 0;
            DWORD64 offset = 0;
            LPVOID view = NULL;
            LPCTSTR shared_name = NULL;

        public:
            MapMemory(MapMemory&)=delete;
            MapMemory(MapMemory&&)=delete;
            //Mapping operator =(Mapping&)=delete;
            MapMemory& operator =(MapMemory&)=delete;

            MapMemory(bool exist,LPCTSTR shared_name,DWORD64 offset=0,DWORD64 size=0,File *file=NULL) {
                this->shared_name = shared_name;
                this->offset = offset;
                this->size = size;
                this->file = file;
                if(exist) {
                    this->map_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS|FILE_MAP_EXECUTE,
                                                                       FALSE,
                                                                       this->shared_name
                                                                       );
                } else {
                    HANDLE map0_handle = INVALID_HANDLE_VALUE;
                    if(this->file) {
                        map0_handle = this->file->get_handle();
                    }
                    this->map_handle = CreateFileMapping(map0_handle,
                                                         NULL,
                                                         PAGE_EXECUTE_READWRITE,
                                                         this->size >> 32,
                                                         (DWORD)this->size,
                                                         reinterpret_cast<LPCTSTR>(this->shared_name)
                                                         );
                }
                if(!this->map_handle) {
                    throw Exception(TEXT("创建映射失败"));
                }
                this->view = MapViewOfFile(this->map_handle,
                                                       FILE_MAP_ALL_ACCESS|FILE_MAP_EXECUTE,
                                                       this->offset>>32,
                                                        (DWORD)this->offset,
                                                       this->size
                                                       );
                if(!this->view) {
                    throw Exception(TEXT("创建映射视图失败"));
                }
            }
            /*LPVOID data(){
            return this->view;
        }*/
            template<class T>
            T data() {
                return static_cast<T>(this->view);
            }
            HANDLE get_handle() {
                return this->map_handle;
            }
            void close_handle() {
                CloseHandle(this->map_handle);
                this->map_handle = NULL;
            }
            void close_view() {
                UnmapViewOfFile(this->view);
                this->view = NULL;
            }
            virtual ~MapMemory() {
                if(this->view) {
                    close_view();
                }
                if(this->map_handle) {
                    close_handle();
                }
            }
    }
    ;
    class Process {
        HANDLE process_handle=NULL;
        public:
                Process(bool exist,DWORD processId) {
            if(exist) {
                process_handle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,processId);
            } else {

            }
        }
        HANDLE get_handle() {
            return process_handle;
        }
        void close_handle() {
            CloseHandle(process_handle);
            process_handle = NULL;
        }
        virtual ~Process() {
            if(process_handle) {
                close_handle();
            }
        }
    }
    ;
    class ProcessMemory final{
        Process *process = NULL;
        public:
                ProcessMemory(Process *p) {
            this->process = p;
        }

        LPVOID alloc(SIZE_T size) {
            LPVOID allocAddress = VirtualAllocEx(this->process->get_handle(),
                                                           NULL,
                                                           size,
                                                           MEM_COMMIT,
                                                           PAGE_EXECUTE_READWRITE
                                                             );
            if(!allocAddress) {
                throw Exception(TEXT("给进程分配内存失败!"));
            }

            return allocAddress;
        }

        SIZE_T write(LPVOID address,LPVOID buffer,SIZE_T size) {
            SIZE_T writtenNum = 0;
            if(!WriteProcessMemory(this->process->get_handle(),
                                           address,
                                           buffer,
                                           size,
                                           &writtenNum
                                                )) {
                throw Exception(TEXT("写入内存数据失败"));
            }
            return writtenNum;
        }
        SIZE_T read(LPCVOID address,LPVOID buffer,SIZE_T size) {
            SIZE_T readNum = 0;
            if(!ReadProcessMemory(this->process->get_handle(),
                                               address,
                                               buffer,
                                               size,
                                               &readNum)) {
                throw Exception(TEXT("读取内存数据失败"));
            }
            return readNum;
        }
    }
    ;
    class RemoteThread final{
        Process *process = NULL;
        LPTHREAD_START_ROUTINE start_address = NULL;
        HANDLE thread_handle = NULL;
        LPVOID param = NULL;
        public:
        RemoteThread(Process *p,LPTHREAD_START_ROUTINE start_address,LPVOID param) {
            this->process = p;
            this->start_address = start_address;
            this->param = param;
        }
        DWORD remote_call() {
            DWORD threadId = -1;
            this->thread_handle = CreateRemoteThread(this->process->get_handle(),
                                                                 NULL,
                                                                 0,
                                                                 start_address,
                                                                 param,
                                                                 0,
                                                                 &threadId
                                                                 );
            if(!this->thread_handle) {
                throw Exception(TEXT("创建进程线程失败"));
            }
            return threadId;
        }

        DWORD wait(DWORD dwMilliseconds=INFINITE){
            return WaitForSingleObject(this->thread_handle,dwMilliseconds);
        }

        virtual ~RemoteThread(){
            if(this->thread_handle){
                CloseHandle(this->thread_handle);
            }
        }
    }
    ;
}
/*
#include<iostream>
using namespace std;
int main(){
    xy::Mapping m(0,1024);
    int* d = m.data<int*>();
    d[0] = 100;
    cout << d[0] << endl;
    xy::File f("test1.txt");
    xy::Mapping m2(0,1024,&f);
    int* d2 = m2.data<int*>();
    d2[1]=100;
    cout << d2[1] << endl;
    DWORD64 *d3 = m2.data<DWORD64*>();
    d3[0] = 999;
    cout << d3[0] << endl;
    cout << d2[0] <<endl;
    cout << d2[1] <<endl;
    char *s = m2.data<char*>();
    for(int i=0;i<1024;i++){
        s[i] = 'A';
    }
    //m2.data<void (*)()>();
    //m2.data<void (*)()>()();
    return 0;
}
*/
#endif // MEM_UTIL_H
